package com.meme.editor.gui.sprite2d;


import java.awt.BorderLayout;
import java.awt.Component;

import javax.swing.JComponent;
import javax.swing.AbstractAction;
import javax.swing.Icon;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeSelectionModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeNode;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeModelEvent;

import org.apache.log4j.Logger;

import com.meme.app.AppRegistry;
import com.meme.editor.model.sprite2d.Module;
import com.meme.editor.model.sprite2d.Sprite2dFile;
import com.meme.editor.model.sprite2d.ModuleImage;
import com.meme.editor.model.sprite2d.PureModuleGroup;
import com.meme.editor.model.sprite2d.CompositedModuleGroup;
import com.meme.editor.model.sprite2d.ModuleGroup;
import com.meme.editor.model.sprite2d.AnimatedModule;
import com.meme.editor.model.sprite2d.AnimatedModuleGroup;
import com.meme.editor.model.sprite2d.Sprite2dModel;
import com.meme.editor.model.sprite2d.Tiled2dPalette;
import com.meme.editor.model.sprite2d.Tiled2dBackground;
import com.meme.editor.EntityObject;

import com.meme.editor.gui.EntityChildrenListView;
import com.meme.editor.gui.PropertyEditPanel;

import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.event.PopupMenuEvent;


class TreeNodeStatus
{
	boolean mExpanded;
	boolean mSelected;
	
	public TreeNodeStatus ()
	{
		mExpanded = false;
		mSelected = false;
	}
	
	public void setExpanded (boolean expanded)
	{
		mExpanded = expanded;
	}
	
	public void setSelected (boolean selected)
	{
		mSelected = selected;
	}
	
	public boolean isExpended ()
	{
		return mExpanded;
	}
	
	public boolean isSelected ()
	{
		return mSelected;
	}
}

public class Sprite2dTreeView extends JPanel 
	implements TreeSelectionListener, TreeExpansionListener, TreeModelListener
{
	static Logger logger = Logger.getLogger (Sprite2dTreeView.class);

	JTree mTree;
	DefaultMutableTreeNode mRootTreeNode;
	Sprite2dFile mSprite2dFile;
	PropertyEditPanel mTreeNodePropertyPanel;
	
	public Sprite2dTreeView (Sprite2dFile sprite2dFile, PropertyEditPanel propertyPanel)
	{
		mSprite2dFile = sprite2dFile;
		mTreeNodePropertyPanel = propertyPanel;
			
		mRootTreeNode = new DefaultMutableTreeNode(mSprite2dFile.getName());
		DefaultTreeModel treeModel = new DefaultTreeModel(mRootTreeNode);
		treeModel.addTreeModelListener(this);
		
		mTree = new JTree(treeModel);
		mTree.setRootVisible(false);
		//mTree.setEditable(true);
		mTree.getSelectionModel().setSelectionMode
		        (TreeSelectionModel.SINGLE_TREE_SELECTION);
		mTree.setShowsRootHandles(true);		

		mTree.addTreeSelectionListener (this);
		mTree.addTreeExpansionListener (this);
		mTree.addMouseListener (new TreeMouseListener());	

		rebuildTree ();
	
		setLayout(new BorderLayout());
		add(mTree, BorderLayout.CENTER);
	}
	
	protected void rebuildTree ()
	{
//		mRootTreeNode.add(new DefaultMutableTreeNode("Module Images"));
//		mRootTreeNode.add(new DefaultMutableTreeNode("Module Groups"));
//		mRootTreeNode.add(new DefaultMutableTreeNode("Animation Groups"));
//		mRootTreeNode.add(new DefaultMutableTreeNode("Models"));
		
		// 
		mRootTreeNode.removeAllChildren();
		
		for ( int setID = 0; setID < mSprite2dFile.getChildrenCount(); ++ setID )
		{
			EntityObject groupset =  mSprite2dFile.getChildByIndex (setID);
			DefaultMutableTreeNode setnode = new DefaultMutableTreeNode (groupset.getName());
			setnode.setUserObject (groupset);
			mRootTreeNode.add (setnode);
			modifyTreeNodeState (setnode);
			
			for ( int id = 0; id < groupset.getChildrenCount(); ++ id )
			{
				EntityObject entity = groupset.getChildByIndex (id);
				DefaultMutableTreeNode entitynode = new DefaultMutableTreeNode (entity.getName());
				entitynode.setUserObject (entity);
				setnode.add (entitynode);				
				modifyTreeNodeState (entitynode);
			}
		}

		
		//
		mTree.updateUI();
	}
	
	void modifyTreeNodeState (DefaultMutableTreeNode node)
	{
		EntityObject entity = (EntityObject) node.getUserObject();
		if (entity.getUserObject() == null)
			entity.setUserObject( new TreeNodeStatus ());

		TreeNodeStatus tns = (TreeNodeStatus) entity.getUserObject();
		TreeNode[] treeNodes = node.getPath();

		
		
		TreePath treepath = new TreePath (treeNodes);
		if ( tns.isExpended() )
			mTree.expandPath( treepath );
		//else
		//	mTree.collapsePath( treepath );
		
		//if (tns.isSelected())
		//	mTree.setSelectionPath( treepath );
	}

	void tagExpanded (DefaultMutableTreeNode treeNode, boolean expanded)
	{
		Object nodedata = treeNode.getUserObject();
		if ( nodedata != null && nodedata instanceof EntityObject)
			( (TreeNodeStatus) ( ( (EntityObject) nodedata ).getUserObject() ) ).setExpanded(expanded);
		
		for (int i = 0; i < treeNode.getChildCount(); ++ i)
		{
			DefaultMutableTreeNode child = (DefaultMutableTreeNode) treeNode.getChildAt (i);
			tagExpanded (child, expanded);
		}
	}
	
	void tagSelected (DefaultMutableTreeNode treeNode, boolean selected)
	{
		Object nodedata = treeNode.getUserObject();
		if ( nodedata != null && nodedata instanceof EntityObject)
			( (TreeNodeStatus) ( ( (EntityObject) nodedata ).getUserObject() ) ).setSelected(selected);
	}
	
//------------------------------------------------------------------------------
// TreeSelectionListener
//------------------------------------------------------------------------------
	
	public void valueChanged(TreeSelectionEvent e) 
	{
		TreePath oldpath = e.getOldLeadSelectionPath();
		if ( oldpath != null )
		{
			DefaultMutableTreeNode oldSelectedNode = (DefaultMutableTreeNode) oldpath.getLastPathComponent();
			tagSelected (oldSelectedNode, false);
		}


		TreePath newpath = e.getNewLeadSelectionPath();
		if ( newpath != null )
		{
			DefaultMutableTreeNode newSelectedNode = (DefaultMutableTreeNode) newpath.getLastPathComponent();
			tagSelected (newSelectedNode, true);
		}
	}	
	
//------------------------------------------------------------------------------
// TreeSelectionListener
//------------------------------------------------------------------------------

    public void treeExpanded(TreeExpansionEvent e) 
    {
    	TreePath path = e.getPath();
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
		tagExpanded (node, true);
    }

    // Required by TreeExpansionListener interface.
    public void treeCollapsed(TreeExpansionEvent e) 
    {
    	TreePath path = e.getPath();
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
		tagExpanded (node, false);
    }


//------------------------------------------------------------------------------
// TreeModelListener
//------------------------------------------------------------------------------
	
    public void treeNodesChanged(TreeModelEvent e) {
        DefaultMutableTreeNode node;
        node = (DefaultMutableTreeNode)
                 (e.getTreePath().getLastPathComponent());

        /*
         * If the event lists children, then the changed
         * node is the child of the node we have already
         * gotten.  Otherwise, the changed node and the
         * specified node are the same.
         */
        try {
            int index = e.getChildIndices()[0];
            node = (DefaultMutableTreeNode)
                   (node.getChildAt(index));
        } catch (NullPointerException exc) {}

        System.out.println("The user has finished editing the node.");
        System.out.println("New value: " + node.getUserObject());
    }
    public void treeNodesInserted(TreeModelEvent e) {
    }
    public void treeNodesRemoved(TreeModelEvent e) {
    }
    public void treeStructureChanged(TreeModelEvent e) {
    }
	
    
//------------------------------------------------------------------------------
// MouseAdapter
//------------------------------------------------------------------------------

	 class TreeMouseListener extends MouseAdapter
	 {
		 public void mousePressed(MouseEvent e) 
		 {
	         TreePath selPath = mTree.getPathForLocation(e.getX(), e.getY());
	         
	         if ( selPath != null && e.getButton() == MouseEvent.BUTTON1 )
	         {
	        	 showContentPanel (selPath);
	         }

	         if ( selPath != null && e.getButton() == MouseEvent.BUTTON3 )
	         {
	        	 popupContextMenu (selPath, e.getX(), e.getY());
	         }
	     }
	 };
	 
	 class TreePopupMenuListener implements PopupMenuListener
	 {
		 TreePath[] mCurrentSelections;
		 TreePath[] mTempSelections;
		 
		 boolean bCancelled;
		 
		 public TreePopupMenuListener (TreePath tempSelection)
		 {
			 this (new TreePath[] {tempSelection});
		 }
		 
		 public TreePopupMenuListener (TreePath[] tempSelections)
		 {
			 mTempSelections = tempSelections;
			 
			 mCurrentSelections = mTree.getSelectionPaths();
			 mTree.setSelectionPaths(mTempSelections);
			 
			 bCancelled = false;
		 }
		 
		 // Method descriptor #10 (Ljavax/swing/event/PopupMenuEvent;)V
		 public void popupMenuWillBecomeVisible(PopupMenuEvent arg0)
		 {
		 }
		 
		 // Method descriptor #10 (Ljavax/swing/event/PopupMenuEvent;)V
		 public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0)
		 {
			 if ( bCancelled )
				 mTree.setSelectionPaths(mCurrentSelections);
		 }
		  
		 // Method descriptor #10 (Ljavax/swing/event/PopupMenuEvent;)V
		 public void popupMenuCanceled(PopupMenuEvent arg0)
		 {
			 bCancelled = true;
		 }
	 }

	 
	 protected void popupContextMenu (TreePath treePath, int x, int y)
	 {
		 JPopupMenu popMenu = new JPopupMenu();
		 popMenu.addPopupMenuListener(new TreePopupMenuListener(treePath));
		 
		 DefaultMutableTreeNode treenode = (DefaultMutableTreeNode) treePath.getLastPathComponent();
		 
		 EntityObject userobject = (EntityObject) treenode.getUserObject();
		 if ( userobject instanceof Sprite2dFile.ModuleImageSet )
		 {
			 popMenu.add (new NewModuleImageAction (null));
		 }
		 else if ( userobject instanceof Sprite2dFile.PureModuleGroupSet )
		 {
			 popMenu.add (new NewImageModuleGroupAction (null));
		 }
		 else if ( userobject instanceof Sprite2dFile.CompositedModuleGroupSet )
		 {
			 popMenu.add (new NewCompositedModuleGroupAction (null));
		 }
		 else if ( userobject instanceof Sprite2dFile.AnimatedModuleGroupSet )
		 {
			 popMenu.add (new NewAnimationGroupAction (null));
		 }
		 else if ( userobject instanceof Sprite2dFile.ModelSet )
		 {
			 popMenu.add (new NewModelAction (null));
		 }
		 else if ( userobject instanceof Sprite2dFile.TilePaletteSet )
		 {
			 popMenu.add (new NewTilePalette (null)); 
		 }
		 else if ( userobject instanceof Sprite2dFile.TiledBackgroundSet )
		 {
			 popMenu.add (new NewTiledBackground (null));
		 }
		 else
		 {
			 popMenu.add (new RemoveChildAction (userobject, null) );
		 }
		          
         popMenu.show(this, x, y);
	 }
	 
	 protected void showContentPanel (TreePath treePath)
	 {
		 Sprite2dMainPanel mainPanel = Sprite2dMainPanel.getMainPanel(this);
		 //
		 AppRegistry.setRegistry("editor.sprite2d.contentPanel.width", mainPanel.getWidth() - getWidth ());
		 AppRegistry.setRegistry("editor.sprite2d.contentPanel.height", mainPanel.getWidth() - getHeight ());
		 
		 //
		 DefaultMutableTreeNode treenode = (DefaultMutableTreeNode) treePath.getLastPathComponent();
		 
		 EntityObject userobject = (EntityObject) treenode.getUserObject();
		 
		 JComponent contentpanel = null;
		 
		 if ( userobject instanceof ModuleImage )
		 {
			 //contentpanel = new javax.swing.JButton ("ModuleImage");
			 contentpanel = new ModuleImageView ( (ModuleImage) userobject );
		 }
		 else if ( userobject instanceof PureModuleGroup )
		 {
			 //contentpanel = new javax.swing.JButton ("ImageModuleGroup");
			 contentpanel = new PureModuleContentPanel ( (PureModuleGroup) userobject );
		 }
		 else if ( userobject instanceof CompositedModuleGroup )
		 {
			 //contentpanel = new javax.swing.JButton ("ModuleGroup");
			 contentpanel = new CompositedModuleContentPanel ( (CompositedModuleGroup) userobject );
		 }
		 else if ( userobject instanceof AnimatedModuleGroup )
		 {
			 //contentpanel = new javax.swing.JButton ("AnimationGroup");
			 contentpanel = new AnimatedModuleContentPanel ( (AnimatedModuleGroup) userobject );
		 }
		 else if ( userobject instanceof Sprite2dModel )
		 {
			 contentpanel = new javax.swing.JButton ("Sprite2dModel");
		 }
		 else if (userobject instanceof Tiled2dPalette)
		 {
			 contentpanel = new Tiled2dPaletteContentPanel ( (Tiled2dPalette) userobject );
		 }
		 else if (userobject instanceof Tiled2dBackground)
		 {
			 contentpanel = new Tiled2dBackgroundContentPanel ( (Tiled2dBackground) userobject );
		 }
		 else
		 {
			 contentpanel = new EntityChildrenListView (userobject); 
			 //( (EntityChildrenListView) contentpanel).setPropertyEditPanel(mTreeNodePropertyPanel);
		 }
		 
		 mTreeNodePropertyPanel.setDataSource (userobject);
		 mainPanel.changeContentPanel (contentpanel);
	 }

	 
//-----------------------------------------------------------------------------------
// actions
//-----------------------------------------------------------------------------------

	class RemoveChildAction extends AbstractAction 
	{
		EntityObject mChild;
		
		public RemoveChildAction (EntityObject child, Icon icon)
		{
			super ("Delete", icon);
			
			mChild = child;
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
	    	mChild.remove ();
	    	rebuildTree ();
	    }
	}
	
	class NewModuleImageAction extends AbstractAction 
	{
		public NewModuleImageAction (Icon icon)
		{
			super ("New Module Image", icon);
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
	    	mSprite2dFile.createNewModuleImage();
	    	rebuildTree ();
	    }
	}	
	 
	class NewImageModuleGroupAction extends AbstractAction 
	{
		public NewImageModuleGroupAction (Icon icon)
		{
			super ("New Image Module Group", icon);
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
	    	mSprite2dFile.createNewPureModuleGroup();
	    	rebuildTree ();
	    }
	}
	 
	class NewCompositedModuleGroupAction extends AbstractAction 
	{
		public NewCompositedModuleGroupAction (Icon icon)
		{
			super ("New Composited Module Group", icon);
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
	    	mSprite2dFile.createNewCompositedModuleGroup();
	    	rebuildTree ();
	    }
	}
	 

	 
	class NewAnimationGroupAction extends AbstractAction 
	{
		public NewAnimationGroupAction (Icon icon)
		{
			super ("New Animation Group", icon);
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
	    	mSprite2dFile.createNewAnimatedModuleGroup();
	    	rebuildTree ();
	    }
	}
	 

	 
	class NewModelAction extends AbstractAction 
	{
		public NewModelAction (Icon icon)
		{
			super ("New Model", icon);
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
	    	mSprite2dFile.createNewModel();
	    	rebuildTree ();
	    }
	}
	 
	class NewTilePalette extends AbstractAction 
	{
		public NewTilePalette (Icon icon)
		{
			super ("New Tile Palette", icon);
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
	    	mSprite2dFile.createNewTilePalette();
	    	rebuildTree ();
	    }
	}
	 
	class NewTiledBackground extends AbstractAction 
	{
		public NewTiledBackground (Icon icon)
		{
			super ("New Background", icon);
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
	    	mSprite2dFile.createNewTiledBackground();
	    	rebuildTree ();
	    }
	}
	 

	 

}
